package com.leedarson.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.FileType;
import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * 引擎的主要工作是根据配置生成代码，二开中新增了前端代码部分的生成。因此这里就把生成前端目录结构的动作加入其中，同时也会根据配置生成部门固定内容的代码，比如app.vue等<br>
 *
 * @author howard
 * @Create_Date: 2020年5月25日下午2:01:13
 * @Modified_By: howard
 * @Modified_Date: 2020年5月25日下午2:01:13
 * @Why_and_What_is_modified: <br>
 */
public class LeedarsonTemplateEngine extends FreemarkerTemplateEngine {

	Logger log = LoggerFactory.getLogger(this.getClass());

	private Configuration configuration;

	@Override
	public LeedarsonTemplateEngine init(ConfigBuilder configBuilder) {
		super.init(configBuilder);
		configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
		configuration.setDefaultEncoding(ConstVal.UTF8);
		configuration.setClassForTemplateLoading(LeedarsonTemplateEngine.class, StringPool.SLASH);
		return this;
	}

	/**
	 * 需要增加输出前端文件的代码。
	 */
	@Override
	public AbstractTemplateEngine batchOutput() {
		try {
			// 统一的前端配置，不需要进入表循环
			generateFrontendCfg();
			//
			List<TableInfo> tableInfoList = getConfigBuilder().getTableInfoList();
			for (TableInfo tableInfo : tableInfoList) {
				Map<String, Object> objectMap = this.getObjectMap(tableInfo);
				Map<String, String> pathInfo = getConfigBuilder().getPathInfo();
				TemplateConfig template = getConfigBuilder().getTemplate();
				// 自定义内容
				InjectionConfig injectionConfig = getConfigBuilder().getInjectionConfig();
				if (null != injectionConfig) {
					injectionConfig.initTableMap(tableInfo);
					objectMap.put("cfg", injectionConfig.getMap());
					List<FileOutConfig> focList = injectionConfig.getFileOutConfigList();
					if (CollectionUtils.isNotEmpty(focList)) {
						for (FileOutConfig foc : focList) {
							if (isCreate(FileType.OTHER, foc.outputFile(tableInfo))) {
								writer(objectMap, foc.getTemplatePath(), foc.outputFile(tableInfo));
							}
						}
					}
				}
				// 生成VUE文件，vue文件关联实体bean，需要放在循环内。
				// 前端模块会影响到后台代码的方法数量，所以需要先确定。
				generateEntityVue(objectMap, tableInfo);
				// Mp.java
				String entityName = tableInfo.getEntityName();
				if (null != entityName && null != pathInfo.get(ConstVal.ENTITY_PATH)) {
					String entityFile = String.format(
							(pathInfo.get(ConstVal.ENTITY_PATH) + File.separator + "%s" + suffixJavaOrKt()),
							entityName);
					if (isCreate(FileType.ENTITY, entityFile)) {
						writer(objectMap,
								templateFilePath(template.getEntity(getConfigBuilder().getGlobalConfig().isKotlin())),
								entityFile);
					}
				}
				// MpMapper.java
				if (null != tableInfo.getMapperName() && null != pathInfo.get(ConstVal.MAPPER_PATH)) {
					String mapperFile = String.format((pathInfo.get(ConstVal.MAPPER_PATH) + File.separator
							+ tableInfo.getMapperName() + suffixJavaOrKt()), entityName);
					if (isCreate(FileType.MAPPER, mapperFile)) {
						writer(objectMap, templateFilePath(template.getMapper()), mapperFile);
					}
				}
				// MpMapper.xml
				if (null != tableInfo.getXmlName() && null != pathInfo.get(ConstVal.XML_PATH)) {
					String xmlFile = String.format((pathInfo.get(ConstVal.XML_PATH) + File.separator
							+ tableInfo.getXmlName() + ConstVal.XML_SUFFIX), entityName);
					if (isCreate(FileType.XML, xmlFile)) {
						writer(objectMap, templateFilePath(template.getXml()), xmlFile);
					}
				}
				// IMpService.java
				if (null != tableInfo.getServiceName() && null != pathInfo.get(ConstVal.SERVICE_PATH)) {
					String serviceFile = String.format((pathInfo.get(ConstVal.SERVICE_PATH) + File.separator
							+ tableInfo.getServiceName() + suffixJavaOrKt()), entityName);
					if (isCreate(FileType.SERVICE, serviceFile)) {
						writer(objectMap, templateFilePath(template.getService()), serviceFile);
					}
				}
				// MpServiceImpl.java
				if (null != tableInfo.getServiceImplName() && null != pathInfo.get(ConstVal.SERVICE_IMPL_PATH)) {
					String implFile = String.format((pathInfo.get(ConstVal.SERVICE_IMPL_PATH) + File.separator
							+ tableInfo.getServiceImplName() + suffixJavaOrKt()), entityName);
					if (isCreate(FileType.SERVICE_IMPL, implFile)) {
						writer(objectMap, templateFilePath(template.getServiceImpl()), implFile);
					}
				}
				// MpController.java
				if (null != tableInfo.getControllerName() && null != pathInfo.get(ConstVal.CONTROLLER_PATH)) {
					String controllerFile = String.format((pathInfo.get(ConstVal.CONTROLLER_PATH) + File.separator
							+ tableInfo.getControllerName() + suffixJavaOrKt()), entityName);
					if (isCreate(FileType.CONTROLLER, controllerFile)) {
						writer(objectMap, templateFilePath(template.getController()), controllerFile);
					}
				}
//				// MpController.java
//				if (null != tableInfo.getControllerName() && null != pathInfo.get(ConstVal.CONTROLLER_PATH)) {
//					String controllerFile = String.format((pathInfo.get(ConstVal.CONTROLLER_PATH) + File.separator
//							+ tableInfo.getControllerName() + suffixJavaOrKt()), entityName);
//					if (isCreate(FileType.CONTROLLER, controllerFile)) {
//						writer(objectMap, templateFilePath(template.getController()), controllerFile);
//					}
//				}

			}
			// 循环外生成module文件用以前端路由。
			// 先丢个tableinfo进入兼容以前代码
			TableInfo tableInfo = tableInfoList.get(0);
			Map<String, Object> objectMap = getObjectMap(tableInfo);
			objectMap.put("tablelist", tableInfoList);
			// 把生成的前端模块z整合进路由文件
			this.generateRouterCode2(objectMap);
			//
		} catch (Exception e) {
			logger.error("无法创建文件，请检查配置信息！", e);
		}
		return this;
	}

	/**
	 * 渲染对象 MAP 信息
	 *
	 * @param tableInfo 表信息对象
	 * @return ignore
	 */
	@Override
	public Map<String, Object> getObjectMap(TableInfo tableInfo) {
		Map<String, Object> objectMap = new HashMap<>(30);
		ConfigBuilder config = getConfigBuilder();
		if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
			objectMap.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
			objectMap.put("controllerMappingHyphen",
					com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToHyphen(tableInfo.getEntityPath()));
		}
		objectMap.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
		objectMap.put("config", config);
		objectMap.put("package", config.getPackageInfo());
		GlobalConfig globalConfig = config.getGlobalConfig();
		objectMap.put("author", globalConfig.getAuthor());
		objectMap.put("idType", globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString());
		objectMap.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName());
		objectMap.put("versionFieldName", config.getStrategyConfig().getVersionFieldName());
		objectMap.put("activeRecord", globalConfig.isActiveRecord());
		objectMap.put("kotlin", globalConfig.isKotlin());
		objectMap.put("swagger2", globalConfig.isSwagger2());
		objectMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
		objectMap.put("table", tableInfo);
		objectMap.put("enableCache", globalConfig.isEnableCache());
		objectMap.put("baseResultMap", globalConfig.isBaseResultMap());
		objectMap.put("baseColumnList", globalConfig.isBaseColumnList());
		objectMap.put("entity", tableInfo.getEntityName());
		objectMap.put("entitySerialVersionUID", config.getStrategyConfig().isEntitySerialVersionUID());
		objectMap.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
		objectMap.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
		objectMap.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
		objectMap.put("entityBooleanColumnRemoveIsPrefix",
				config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
		objectMap.put("superEntityClass", getSuperClassName(config.getSuperEntityClass()));
		objectMap.put("superMapperClassPackage", config.getSuperMapperClass());
		objectMap.put("superMapperClass", getSuperClassName(config.getSuperMapperClass()));
		objectMap.put("superServiceClassPackage", config.getSuperServiceClass());
		objectMap.put("superServiceClass", getSuperClassName(config.getSuperServiceClass()));
		objectMap.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
		objectMap.put("superServiceImplClass", getSuperClassName(config.getSuperServiceImplClass()));
		objectMap.put("superControllerClassPackage", verifyClassPacket(config.getSuperControllerClass()));
		objectMap.put("superControllerClass", getSuperClassName(config.getSuperControllerClass()));
		// 二开内容
		// 增加map中对前端模块化生成的支持。
		InjectionConfig injectionConfig = config.getInjectionConfig();
		Map<String, Object> InJectMapmap = injectionConfig.getMap();
//		String tablename = tableInfo.getName();
//		Object object = InJectMapmap.get("frontend");
		// frontend里面包着user表名,user里包着各种模块对应的配置如list,details等
		// 这里的工作就是吧这一层直接暴露在循环bean的frontend key 里面。
//		Optional.ofNullable(object).ifPresent(e -> {
//			Map fmap = (Map) e;
//			Object t2 = fmap.get(tablename);
//			Optional.ofNullable(t2).ifPresent(o -> {
//				objectMap.put("frontend", t2);
//			});
//		});
		//
		// 新的二开内容，这一次避免反复的参数设置，直接通过表明拿到配置，设置进objectMap中
		HashMap tableConfigHashMap = FrontEndConfig.getFrontendConfigByTable(tableInfo.getName());
		objectMap.put("f", tableConfigHashMap);
		//
		return Objects.isNull(config.getInjectionConfig()) ? objectMap
				: config.getInjectionConfig().prepareObjectMap(objectMap);
	}

	/**
	 * 获取类名
	 *
	 * @param classPath ignore
	 * @return ignore
	 */
	private String getSuperClassName(String classPath) {
		if (StringUtils.isBlank(classPath)) {
			return null;
		}
		return classPath.substring(classPath.lastIndexOf(StringPool.DOT) + 1);
	}

	/**
	 * 用于渲染对象MAP信息 {@link #getObjectMap(TableInfo)} 时的superClassPacket非空校验
	 *
	 * @param classPacket ignore
	 * @return ignore
	 */
	private String verifyClassPacket(String classPacket) {
		return StringUtils.isBlank(classPacket) ? null : classPacket;
	}
//	public static void main(String[] args) {
//		URL resource = LeedarsonTemplateEngine.class.getResource("/");
//		System.out.println(resource);
//		Collection<File> listFiles = FileUtils.listFiles(new File(resource.getPath()), null, true);
//		System.out.println(listFiles.size());
//		listFiles.forEach(System.out::println);
//	}

//	public static void main(String[] args) {
//		File dir = new File(
//				"E:\\git\\myutilstore\\mybatis-plus-sample-generator\\src\\main\\resources\\templates\\frontend\\src");
//		Collection<File> listFiles = FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
//		listFiles.forEach(f -> {
//			String name = f.getName();
//			String absolutePath = f.getAbsolutePath();
//			if (!StringUtils.endsWith(name, ".ftl")) {
//				try {
//					File destFile = new File(absolutePath + ".ftl");
//					if (!destFile.exists()) {
//						FileUtils.copyFile(f, destFile);
//					}
//				} catch (IOException e) {
//					e.printStackTrace();
//				}
//			}
//		});
//	}

//	public static void main(String[] args) {
//		LeedarsonTemplateEngine e = new LeedarsonTemplateEngine();
////		ClassPathResource classPathResource = new ClassPathResource("/",LeedarsonTemplateEngine.class);
////		System.out.println(classPathResource.getPath());
//		String dir_path = LeedarsonTemplateEngine.class.getResource("/").getPath() + "templates/frontend";
//		System.out.println(dir_path);
//	}

	/**
	 * 生成前端配置文件，到这一步骤的时候，前端路径已经构建完毕了。因此读取模板文件开始生成前端文件配置<br>
	 *
	 * @throws IOException
	 *
	 * @auhor howard
	 * @Create_Date: 2020年5月20日下午3:21:36
	 * @last_modify by howard at 2020年5月22日下午3:47:01 <br>
	 * @Why_and_What_is_modified:重构生成逻辑，改为直接从模板文件结构输出，无视pathinfo配置
	 * @throws IOException
	 */
	private void generateFrontendCfg() throws IOException {
		String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
//		 基本输出的前端路径
		String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir, "/src/main/java",
				"/frontend");
		log.info("前端文件输出路径为：" + frontendOut);
		// 模板前端文件路径
		String tplDir = LeedarsonTemplateEngine.class.getResource("/").getPath() + "templates/frontend";
		log.info("模板路径为：" + tplDir);
		// 开始循环按照模板文件输出
		Iterator<File> iterateFiles = FileUtils.iterateFiles(new File(tplDir), TrueFileFilter.TRUE,
				TrueFileFilter.TRUE);
		iterateFiles.forEachRemaining(f -> {
			try {
				// 模板文件路径
				String tplFilePath = f.getCanonicalPath();
				// 输出文件路径
				String substringAfterLast = StringUtils.substringAfterLast(tplFilePath, "frontend");
				String noExtFile = StringUtils.remove(substringAfterLast, ".ftl");
//				//eclipse里java编译打包不会把.editorconfig这种文件输出到class，因此要做一点配置和更改。
//				if (StringUtils.contains(noExtFile, "noname")) {
////					System.out.println(noExtFile);
//					noExtFile = StringUtils.remove(noExtFile, "noname");
//				}
				// 最终需要输出的文件
				String outPutFile = frontendOut + noExtFile;
				File outFile = new File(outPutFile);
				if (outFile.exists()) {// 为了避免惨剧加入判断
					log.info(outPutFile + "文件存在，跳过生成文件。");
					return;
				}
				try (FileOutputStream fileOutputStream = new FileOutputStream(outPutFile)) {
					String tplRelatePath = "/templates/frontend" + substringAfterLast;
					String tplPath = StringUtils.replace(tplRelatePath, "\\", "/");
					// 注意到这里其实是class的根目录，因此模版路径必须是相对路径
					Template template = configuration.getTemplate(tplPath);
					ConfigBuilder configBuilder = getConfigBuilder();
					InjectionConfig injectionConfig = configBuilder.getInjectionConfig();
					Map<String, Object> objectMap = injectionConfig.getMap();
					template.process(objectMap, new OutputStreamWriter(fileOutputStream, ConstVal.UTF8));
					log.info(outPutFile + "输出成功");
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		});

		// TODO 缺个判断，后期再补。
//		Map<String, String> pathInfo = getConfigBuilder().getPathInfo();
//		String outPutBuildPath = pathInfo.get("frontend");
//		JSONObject frontEndTpl = JSONObject.parseObject(outPutBuildPath);
//		frontEndTpl.forEach((dirName, dirOutPutPath) -> {
//			String[] ext = { "ftl" };
//			// 模板文件相对位置，因为结构一样，所以从输出配置里替换下。
//			String tplRelatePath = StringUtils.substringAfter(dirOutPutPath.toString(), "frontend");
//			// 路径取class路径
//			String dir_path = LeedarsonTemplateEngine.class.getResource("/").getPath() + "/templates/frontend"
//					+ tplRelatePath;
//			File directory = new File(dir_path);
//			if (!directory.isDirectory()) {
//				log.info(dir_path + "不是有效路径或不包括模板，跳过生成文件。");
//				return;
//			}
//			Collection<File> files = FileUtils.listFiles(directory, ext, false);
//			files.forEach(tpl -> {
//				String tplPath = tpl.getPath();
//				try {
//					tplPath = StringUtils.replace(tplPath, "\\", "/");
//					Template template = configuration
//							.getTemplate("templates/frontend" + tplRelatePath + "/" + tpl.getName());
//					String filename = StringUtils.remove(tpl.getName(), ".ftl");
//					File file = new File(dirOutPutPath + "/" + filename);
//					if (file.exists()) {// 为了避免惨剧加入判断
//						log.info(filename + "文件存在，跳过生成文件。");
//						return;
//					}
//					try (FileOutputStream fileOutputStream = new FileOutputStream(dirOutPutPath + "/" + filename)) {
//						template.process(new HashMap<>(), new OutputStreamWriter(fileOutputStream, ConstVal.UTF8));
//					}
//				} catch (Exception e) {
//					e.printStackTrace();
//				}
//			});
//		});
	}

	// 生成前端模板文件老方法,通过读取pathinfo顺序来生成文件，后面想想还是算了，直接读取模板目录生成他不香吗？
	/*
	 * private void generateFrontendCfg() throws IOException { // TODO 缺个判断，后期再补。
	 * Map<String, String> pathInfo = getConfigBuilder().getPathInfo(); String
	 * outPutBuildPath = pathInfo.get("frontend"); JSONObject frontEndTpl =
	 * JSONObject.parseObject(outPutBuildPath); frontEndTpl.forEach((dirName,
	 * dirOutPutPath) -> { String[] ext = { "ftl" }; //模板文件相对位置，因为结构一样，所以从输出配置里替换下。
	 * String tplRelatePath = StringUtils.substringAfter(dirOutPutPath.toString(),
	 * "frontside"); // 路径取class路径 String dir_path =
	 * LeedarsonTemplateEngine.class.getResource("/").getPath() +
	 * "/templates/frontend" + tplRelatePath; File directory = new File(dir_path);
	 * if (!directory.isDirectory()) { log.info(dir_path + "不是有效路径或不包括模板，跳过生成文件。");
	 * return; } Collection<File> files = FileUtils.listFiles(directory, ext,
	 * false); files.forEach(tpl -> { String tplPath = tpl.getPath(); try { tplPath
	 * = StringUtils.replace(tplPath, "\\", "/"); Template template = configuration
	 * .getTemplate("templates/frontend" + tplRelatePath + "/" + tpl.getName());
	 * String filename = StringUtils.remove(tpl.getName(), ".ftl"); File file = new
	 * File(dirOutPutPath + "/" + filename); if (file.exists()) {// 为了避免惨剧加入判断
	 * log.info(filename + "文件存在，跳过生成文件。"); return; } try (FileOutputStream
	 * fileOutputStream = new FileOutputStream(dirOutPutPath + "/" + filename)) {
	 * template.process(new HashMap<>(), new OutputStreamWriter(fileOutputStream,
	 * ConstVal.UTF8)); } } catch (Exception e) { e.printStackTrace(); } }); }); }
	 */

	/**
	 * 根据当前循环的table生成对应前端的代码<br>
	 *
	 * @auhor howard
	 * @Create_Date: 2020年5月29日下午4:11:55
	 * @last_modify by howard at 2020年5月29日下午4:11:55 <br>
	 * @Why_and_What_is_modified:
	 * @param objectMap
	 * @throws Exception
	 */
	@Deprecated
	private void generateRouterCode(Map<String, Object> objectMap) throws Exception {
		String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
//		 基本输出的前端路径
		String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir, "/src/main/java",
				"/frontend/src/router/module.js");
		// 模板文件
		String tplRelatePath = "/templates/module.js.ftl";
		tplRelatePath = StringUtils.replace(tplRelatePath, "\\", "/");
//		outp
		writer(objectMap, tplRelatePath, frontendOut);
	}

	/**
	 * 前端模块的生成方式从自动改为配置页面选择，因此这里需要读取出配置页面配置的信息，再进行生成路由。 <br>
	 *
	 * @auhor howard
	 * @Create_Date: 2020年10月14日下午2:59:25
	 * @last_modify by howard at 2020年10月14日下午2:59:25 <br>
	 * @Why_and_What_is_modified:
	 * @param objectMap
	 * @throws Exception
	 */
	private void generateRouterCode2(Map<String, Object> objectMap) throws Exception {
		String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
		// 基本输出的前端路径
		String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir, "/src/main/java",
				"/frontend/src/router/module.js");
		// 模板文件
		String tplRelatePath = "/templates/module.js.ftl";
		tplRelatePath = StringUtils.replace(tplRelatePath, "\\", "/");
		// 组装数据
		HashMap<String, HashMap<String, Object>> module = FrontEndConfig.getFrontendModule();
		Map<String, Object> tplmap = new HashMap<String, Object>();
		ArrayList<String> list = new ArrayList<String>();
		module.forEach((key, map) -> {
			map.forEach((k2, v) -> {
				if (!StringUtils.equals(k2, "noFE")) {
					String moduleName = key + k2;
					list.add(moduleName);
				}
			});
		});
		tplmap.put("frouter", list);
		objectMap.putAll(tplmap);
		//
		// outp
		writer(objectMap, tplRelatePath, frontendOut);
	}

	/**
	 * 生成VUE文件<br>
	 *
	 * @auhor howard
	 * @Create_Date: 2020年5月29日下午4:48:55
	 * @last_modify by howard at 2020年5月29日下午4:48:55 <br>
	 * @Why_and_What_is_modified:
	 * @param objectMap
	 * @throws Exception
	 */
	@Deprecated
	private void generateEntityVue2(Map<String, Object> objectMap) throws Exception {
		// 先生成component
		// component文件输出路径为
		String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
//		 基本输出的前端路径
		String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir, "/src/main/java",
				"/frontend/src/components/");
		String tablenameL = (String) objectMap.get("entity");
		tablenameL = StringUtils.lowerCase(tablenameL);
		frontendOut = frontendOut + tablenameL + ".vue";
		frontendOut = StringUtils.replace(frontendOut, "\\", "/");
		// 注意到这里其实是class的根目录，因此模版路径templateFilePath必须是相对路径
		// component文件模板的路径为
		String tplRelatePath = "/templates/entity.vue.ftl";
		tplRelatePath = StringUtils.replace(tplRelatePath, "\\", "/");
		writer(objectMap, tplRelatePath, frontendOut);
	}

//	@Deprecated
//	private void generateEntityVue1(Map<String, Object> objectMap) throws Exception {
//		// 先生成component
//		// component文件输出路径为
//		String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
////		 基本输出的前端路径
//		final String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir, "/src/main/java",
//				"/frontend/src/components/");
//		// 输出指定的前端模块。
//		// 先输出列表模块
//		JSONArray listArray = LeedarsonGenerator.frontsideConfig.getJSONArray("list");
//		listArray.forEach(e -> {
//			JSONObject o = (JSONObject) e;
//			String tablename = o.getString("table").replace("-", "").replace("_", "").toLowerCase() + "list";
//			JSONArray listcolumn = o.getJSONArray("listcolumn");
//			JSONArray querycolumn = o.getJSONArray("querycolumn");
//			tablename = StringUtils.lowerCase(tablename);
//			String out = frontendOut + tablename + ".vue";
//			out = StringUtils.replace(out, "\\", "/");
//			// 注意到这里其实是class的根目录，因此模版路径templateFilePath必须是相对路径
//			// component文件模板的路径为
//			String tplRelatePath = "/templates/list.vue.ftl";
//			objectMap.put("tablename", tablename);
//			objectMap.put("listcolumn", listcolumn.toArray());
//			objectMap.put("querycolumn", querycolumn.toArray());
//			try {
//				writer(objectMap, tplRelatePath, out);
//			} catch (Exception e1) {
//				e1.printStackTrace();
//			}
//		});
//		// 留着以后其他类型的模块生成。
//	}

	/**
	 * 用更合适的方式进行前端文件的输出<br>
	 * 这里修改了很多遍，最新的设计是直接读取frontendConfig里的配置信息，根据当前循环中表取得对应前端渲染信息，开始该表的前端模块代码生成。
	 * <br>
	 * 生成之后的前端文件放在前端组件的默认目录下，而前端配置信息放在objectMap的f key 下面。
	 *
	 * @param tableInfo
	 *
	 * @auhor 86136
	 * @Create_Date: 2020年10月3日下午10:26:07
	 * @last_modify by 86136 at 2020年10月3日下午10:26:07 <br>
	 * @Why_and_What_is_modified:
	 * @param
	 * @throws @param    objectMap
	 * @throws Exception
	 */
	private void generateEntityVue(Map<String, Object> objectMap, TableInfo tableInfo) throws Exception {
		TableInfo info = (TableInfo) objectMap.get("table");
		final String tableName = info.getName();
		final String EntitylowerCase = info.getEntityName().toLowerCase();// 表名会带下划线
		HashMap<String, Object> frontendConfig = FrontEndConfig.getFrontendConfigByTable(tableName);
//		这里的工作还要加上，判断当前objectMaP有哪些对应的前端模块，
		// 这里得到的应该是各种类型的前端模块配置
		// 当不存在前端配置的时候，他会是null的。
		Optional.ofNullable(frontendConfig).ifPresent(f -> {
			f.forEach((k, v) -> {
				if (StringUtils.equals(k, "noFE")) {
					return;
				}
				// 这里的key是当前循环table中所配置的特定前端模块类型，如list
				//
				String globaleOutputDir = getConfigBuilder().getGlobalConfig().getOutputDir();
				final String frontendOut = org.springframework.util.StringUtils.replace(globaleOutputDir,
						"/src/main/java", "/frontend/src/components/");

				// 输出路径
				String out = frontendOut + EntitylowerCase + k + ".vue";
				String tplRelatePath = "/templates/fmodule/" + k + ".vue.ftl";
				try {
					// objectMap.put("f", v);// 设置为前端的配置都在f的这个key之下 这样做会让vue模版和java模板的内置配置不统一，删除。
					objectMap.put("tablename", EntitylowerCase);
					writer(objectMap, tplRelatePath, out);
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		});
	}

	@Override
	public void writer(Map<String, Object> objectMap, String templatePath, String outputFile) throws Exception {
		File f = new File(outputFile);
		if (f.exists()) {
			log.info("文件:" + outputFile + "存在，为了避免惨剧发生，跳过生成文件");
			return;
		}
		Template template = configuration.getTemplate(templatePath);
		try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {
			template.process(objectMap, new OutputStreamWriter(fileOutputStream, ConstVal.UTF8));
		}
		log.info("模板:" + templatePath + ";  文件:" + outputFile + " 输出完毕");
	}

	@Override
	public String templateFilePath(String filePath) {
		return filePath + ".ftl";
	}

	/**
	 * 处理输出目录
	 */
	public LeedarsonTemplateEngine mkdirs() {
		getConfigBuilder().getPathInfo().forEach((key, value) -> {
			File dir = new File(value);
			if (!dir.exists()) {
				boolean result = dir.mkdirs();
				if (result) {
					logger.debug("创建目录： [" + value + "]");
				}
			}
			// 新增前端的配置
			if (StringUtils.equals(key, "frontend")) {
				JSONObject.parseObject(value).forEach((jv, jk) -> {
					File dir2 = new File(jk.toString());
					if (!dir2.exists()) {
						boolean result = dir2.mkdirs();
						if (result) {
							logger.debug("创建目录： [" + jk + "]");
						}
					}
				});
			}
		});
		return this;
	}

}
